/* eslint-disable no-case-declarations */
import { UploadFile, UploadInput, UploadOutput, UploadServerService, UploadStatus, UploaderOptions } from "../composition/type";
import { Observable, Subject, Subscription } from "rxjs";
import { mergeMap, finalize } from "rxjs/operators";
import { isContentTypeAllowed, isFileSizeAllowed, makeUploadFile } from "../composition/utils";

export class FFileUploaderService {
    queue: UploadFile[];

    serviceEvents: Subject<UploadOutput>;

    uploadScheduler: Subject<{
        event: UploadInput;
        files: UploadFile[];
    }>;

    subs: { id: string; sub: Subscription; ids?: string[] }[];

    uploadOpts: UploaderOptions = {
        // 默认不限制,0代表不限制 Number.POSITIVE_INFINITY
        allowedContentTypes: ["*"],
        maxUploads: 0,
        /** 单位M，默认是12M,0代表不限制 */
        maxFileSize: 12,
        concurrency: Number.POSITIVE_INFINITY,
        uploadedCount: 0
    };

    private extendServerConfig = null;

    constructor(private uploadServerSer: UploadServerService) {

        this.queue = [];
        this.serviceEvents = new Subject();
        this.uploadScheduler = new Subject();
        this.subs = [];
        this.uploadScheduler
            .pipe(
                mergeMap((upload) => {
                    return this.serverMethod(upload);
                }, this.uploadOpts.concurrency)
            )
            .subscribe((uploadOutput) => {
                if (uploadOutput.type === "removed") {
                    // 事件中返回的都是
                    this.queue = this.queue.filter(
                        (item) => item.progress.status !== UploadStatus.Remove
                    );
                    if (!uploadOutput.hasOwnProperty("message")) {
                        uploadOutput.message = "被删除";
                    }
                }
                if (uploadOutput.type === "error") {
                    // 上传失败的附件移除
                    this.queue = this.queue.filter((queueItem) => {
                        return (uploadOutput.files && uploadOutput.files.findIndex((item) => queueItem.id == item.id) < 0);
                    });
                }
                this.serviceEvents.next(uploadOutput);
            });

    }

    getEvents() {
        return this.serviceEvents;
    }

    setOptions(options: UploaderOptions) {
        // 重置文件大小、类型、个数限制
        if (options) {
            this.uploadOpts = Object.assign(this.uploadOpts, options);
        }
    }

    handleFiles(incomingFiles: FileList): void {
        const allowedIncomingFiles: File[] = [].reduce.call(
            incomingFiles,
            (acc: File[], checkFile: File, i: number) => {
                const futureQueueLength = acc.length + this.queue.length + 1;
                const judgeResult = this.rejectedReason(
                    checkFile.name,
                    futureQueueLength,
                    checkFile.size
                );
                if (judgeResult.allowed) {
                    acc = acc.concat(checkFile);
                } else {
                    // 不符合当前文件类型或者内容超出限制，抛出事件
                    const rejectedFile: UploadFile = makeUploadFile(
                        checkFile,
                        i
                    );
                    this.serviceEvents.next({
                        type: "rejected",
                        file: rejectedFile,
                        message: judgeResult.message,
                    });
                }
                return acc;
            },
            []
        );

        // 构造文件结构，并单个抛出事件
        [].map.call(allowedIncomingFiles, (file: File, i: number) => {
            const uploadFile: UploadFile = makeUploadFile(file, i);
            this.queue.push(uploadFile);
            this.serviceEvents.next({ type: "addedToQueue", file: uploadFile });
        });

        // 所有的文件都已经添加，抛出事件
        this.serviceEvents.next({ type: "allAddedToQueue" });
    }

    /**
     * 获取上传被拒绝的理由
     * @param name
     * @param queuelength
     * @param size
     * @returns
     */
    rejectedReason(name: string, queuelength: number, size: number) {
        let allowed = false;
        let message = "";
        // 已存在同名文件
        const findDuplicateIndex = this.queue.findIndex((file) => file.name == name);
        if (findDuplicateIndex > -1) {
            message = "上传失败：已存在同名文件";
        } else if (!isContentTypeAllowed(this.uploadOpts.allowedContentTypes || [], name)) {
            message =
                "上传失败：只允许上传" +
                (this.uploadOpts.allowedContentTypes || []).join(",") +
                "类型的文档";
        } else if (
            this.uploadOpts.maxUploads > 0 &&(
                this.uploadOpts.maxUploads <= this.uploadOpts.uploadedCount ||
            queuelength + this.uploadOpts.uploadedCount > this.uploadOpts.maxUploads)) {
            message = "上传失败：文件总个数超出" + this.uploadOpts.maxUploads + "限制";
        } else if (!isFileSizeAllowed(this.uploadOpts.maxFileSize, size)) {
            message = "上传失败：单个文件大小超出" + this.uploadOpts.maxFileSize + "MB的限制";
        } else if (size == 0) {
            message = "上传失败：不允许文件为空";
        } else {
            allowed = true;
        }
        return {
            allowed,
            message,
        };
    }

    /**
     * 从前端传来事件，进行服务器端方法类型判断
     * @param input
     */
    initInputEvents(input: Subject<UploadInput>): Subscription {
        // debugger
        return input.subscribe((event: UploadInput) => {
            // debugger
            switch (event.type) {
            case "upload":
                const uploadFileIndex = this.queue.findIndex(
                    (file) => file === event.file
                );
                if (uploadFileIndex !== -1 && event.file) {
                    this.uploadScheduler.next({
                        files: [this.queue[uploadFileIndex]],
                        event,
                    });
                }
                break;
            case "cancel":
                const id = event.id || null;
                if (!id) {
                    return;
                }
                const fileIndex = this.queue.findIndex((file) => file.id === id);
                if (fileIndex !== -1) {
                    this.serviceEvents.next({
                        type: "cancelled",
                        files: [this.queue[fileIndex]],
                        message: "已取消附件上传",
                    });
                    this.queue.splice(fileIndex, 1);
                }
                break;
            case "hide":
                if (!event.id) {
                    return;
                }
                const ids = event.id.split(",");
                this.queue = this.queue.filter((file) => {
                    const tIndex = ids.findIndex((tId) => tId === file.id);
                    return !(tIndex > -1);
                });
                break;
            case "cancelAll":
                // 取消，直接从队列中移除，不用修改状态
                const queueFiles = this.queue.filter(
                    (uploadFile) => uploadFile.progress.status === UploadStatus.Queue
                );
                if (queueFiles.length) {
                    this.serviceEvents.next({
                        type: "cancelled",
                        files: queueFiles,
                        message: "已取消附件上传",
                    });
                    this.queue = this.queue.filter(
                        (uploadFile) => uploadFile.progress.status !== UploadStatus.Queue
                    );
                }
                break;
            case "remove":
                if (!event.id) {
                    return;
                }
                const removeIndex = this.queue.findIndex(
                    (file) => file.id === event.id
                );
                if (removeIndex !== -1) {
                    // 得有个开始删除和已经删除
                    this.queue[removeIndex].progress.status = UploadStatus.Remove;
                    this.uploadScheduler.next({
                        files: [this.queue[removeIndex]],
                        event
                    });
                }
                break;
            case "removeAll":
                const removeQueueFiles = this.queue.filter(
                    (uploadFile) => uploadFile.progress.status === UploadStatus.Queue
                );
                if (removeQueueFiles.length) {
                    this.serviceEvents.next({
                        type: "cancelled",
                        files: removeQueueFiles,
                        message: "删除附件成功",
                    });
                    this.queue = this.queue.filter(
                        (uploadFile) => uploadFile.progress.status != UploadStatus.Queue
                    );
                }
                // 正在上传的附件是如何处理
                // const doneFiles = this.queue.filter(uploadFile => uploadFile.progress.status === UploadStatus.Done);
                if (this.queue.length) {
                    event.type = "remove";
                    this.queue.map(
                        (item) => (item.progress.status = UploadStatus.Remove)
                    );
                    this.uploadScheduler.next({
                        files: this.queue,
                        event,
                    });
                }
                break;
            }
        });
    }

    setExtendServerConfig(extendSer) {
        this.extendServerConfig = extendSer;
    }

    serverMethod(upload: {
        event: UploadInput;
        files: UploadFile[];
    }): Observable<UploadOutput> {
        return new Observable((observer) => {
            let sub;
            const ids = upload.files.map((fileItem) => fileItem.id);
            switch (upload.event.type) {
            case "upload":
                sub = this.upload(upload.files, upload.event);
                break;
            case "remove":
                sub = this.remove(upload.files, upload.event);
                break;
            default:
                sub = null;
            }
            if (!sub) {
                return;
            }
            sub.pipe(finalize(() => {
                // debugger;
                if (!observer.closed) {
                    observer.complete();
                }
            })).subscribe(
                (output) => {
                    // debugger;
                    observer.next(output);
                },
                (err) => {
                    observer.next(err);
                },
                () => {
                    observer.complete();
                }
            );
            this.subs.push({ ids, sub, id: "" });
        });
    }

    upload(files: UploadFile[], event: UploadInput): Observable<UploadOutput> {
        // 抛出开始上传的事件
        this.serviceEvents.next({ type: "start", files });
        return this.uploadServerSer.upload(files, event, this.extendServerConfig);
    }

    remove(files: UploadFile[], event: UploadInput): Observable<UploadOutput> {
        return this.uploadServerSer.remove(files, event, this.extendServerConfig);
    }

    /**
     * 重置
     */
    reset() {
        this.queue = [];
        // this.uploadScheduler = new Subject();
        // this.subs.forEach(sub => {
        //   if (sub.sub) {
        //     sub.sub.unsubscribe();
        //   }
        // });
        this.subs = [];
    }

    /**
     * 销毁
     */
    destroyed() {
        this.serviceEvents.unsubscribe();
        this.uploadScheduler.unsubscribe();
        this.reset();
    }
}
